// @Author EthanScriptOn
// @Desc
package config_source_loader_management

import (
	"errors"
	"gitee.com/fatzeng/srf_switch_basic_components/util"
	"gitee.com/fatzeng/srf_switch_config/config_context"
	"gitee.com/fatzeng/srf_switch_config/config_define"
	"gitee.com/fatzeng/srf_switch_config/config_source_loader"
	"reflect"
)

var SourceLoaderType = reflect.TypeOf((*config_source_loader.SourceLoader)(nil)).Elem()

type SourceLoaderManager interface {
	ManageSourceLoaders() config_source_loader.SourceLoaders
	HasSourceLoader(configType config_define.ConfigSourceType) (bool, error)
	GetAllSourceTypes() ([]config_define.ConfigSourceType, error)
	CreateSourceLoader(ctx *config_context.ConfigInitializationContext) error
	LoadAll() ([]interface{}, error)
	Load(configType config_define.ConfigSourceType) (interface{}, error)
}

type SourceProcessorConfiguration struct {
	SourceLoaderType reflect.Type
	ProcessorType    reflect.Type
}

type SimpleSourceLoaderManager struct {
	sourceLoaders config_source_loader.SourceLoaders
	isLoaded      map[config_source_loader.SourceLoader]bool
}

func GenerateSourceLoaderManager(sourceLoaders config_source_loader.SourceLoaders) *SimpleSourceLoaderManager {
	return &SimpleSourceLoaderManager{
		sourceLoaders: sourceLoaders,
	}
}

func (s *SimpleSourceLoaderManager) ManageSourceLoaders() config_source_loader.SourceLoaders {
	return s.sourceLoaders
}

func (s *SimpleSourceLoaderManager) HasSourceLoader(configType config_define.ConfigSourceType) (bool, error) {
	_, err := s.sourceLoaders.GetSourceLoader(configType)
	return err == nil, err
}

func (s *SimpleSourceLoaderManager) GetAllSourceTypes() ([]config_define.ConfigSourceType, error) {
	var types []config_define.ConfigSourceType
	loaders, err := s.sourceLoaders.GetAllSourceLoader()
	if err != nil {
		return types, err
	}
	for _, loader := range loaders {
		types = append(types, loader.GetConfigSourceType())
	}
	return types, nil
}

func (s *SimpleSourceLoaderManager) CreateSourceLoader(ctx *config_context.ConfigInitializationContext) (err error) {
	getList, err := GetSupportedList()
	if err != nil {
		return err
	}
	for _, sourceProcessorConfiguration := range getList {
		sourceLoader, err := s.MakeSourceLoader(sourceProcessorConfiguration.SourceLoaderType)
		if err != nil {
			return
		}
		if err = sourceLoader.SetSource(ctx); err != nil {
			return
		}
		if err = sourceLoader.Initialization(); err != nil {
			return
		}
		if err = s.AddSourceLoader(sourceLoader); err != nil {
			return
		}
	}
	return
}

func (s *SimpleSourceLoaderManager) MakeSourceLoader(sourceLoaderType reflect.Type) (config_source_loader.SourceLoader, error) {
	if !sourceLoaderType.Implements(SourceLoaderType) {
		return nil, errors.New("the type must implement SourceLoader interface")
	}
	newInstance := util.NewInstance(sourceLoaderType).Interface()
	sourceLoader, ok := newInstance.(config_source_loader.SourceLoader)
	if !ok {
		return nil, errors.New("conversion exception the type must implement SourceLoader interface")
	}
	return sourceLoader, nil
}

func (s *SimpleSourceLoaderManager) AddSourceLoader(sourceLoader config_source_loader.SourceLoader) error {
	if sourceLoader == nil {
		return errors.New("the sourceLoader cannot be nil")
	}
	return s.sourceLoaders.AddSourceLoader(sourceLoader)
}

func (s *SimpleSourceLoaderManager) Load(configType config_define.ConfigSourceType) (interface{}, error) {
	sourceLoader, err := s.sourceLoaders.GetSourceLoader(configType)
	if err != nil {
		return err, nil
	}
	if s.isLoaded[sourceLoader] {
		return nil, errors.New("the loader for the specified config source type has already been loaded")
	}
	loadResult, err := sourceLoader.LoadConfigSource()
	if err != nil {
		return nil, err
	}
	s.isLoaded[sourceLoader] = true
	return loadResult, nil
}

func (s *SimpleSourceLoaderManager) LoadAll() ([]interface{}, error) {
	allSourceLoader, err := s.sourceLoaders.GetAllSourceLoader()
	if err != nil {
		return nil, err
	}
	var loadResults []interface{}
	for _, loader := range allSourceLoader {
		loadResult, err := loader.LoadConfigSource()
		if err != nil {
			return nil, err
		}
		loadResults = append(loadResults, loadResult)
	}
	return loadResults, nil
}
